# -*- coding: utf-8 -*-
#
# Copyright (c) 2011,2012 RMIT e-Research Office
#   (RMIT University, Australia)
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#    *  Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#    *  Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#    *  Neither the name of RMIT University nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

"""
.. module:: Mayer
   :synopsis: Ingests records form the Mayer respository into SMRA

.. moduleauthor:: Ian Thomas <Ian.Edward.Thomas@rmit.edu.au>


"""
from django.conf import  settings
import suds
import logging
import urllib2
import datetime
import time

from smra.smra_portal.models import Schema
from smra.smra_portal.models import System
from smra.smra_portal.models import Repository
from smra.smra_portal.models import ParameterName

from smra.smra_portal.ReposPlugin import Error
from smra.smra_portal.ReposPlugin import InvalidMediaObject
from smra.smra_portal.ReposPlugin import InvalidConfigurationException 
from smra.smra_portal.ReposPlugin import Timer
from smra.smra_portal.ReposPlugin import MissingIDException
from django.utils.http import urlquote_plus


logger = logging.getLogger(__name__)



class ReposConverter(object):
    """ 
    Converts the data on the MAYER website into form that can be added 
    to models
    """

    REPOS_NAME = "Mayer"    
    ns = "http://smra.edu.au/smra/mayer"
    
   
    base_field_trans = {'Title':'Title',
                        'DE':'Subject',
                        'Responsibility':'Responsibility',
                        'BN':'ISBN_ISSN',
                        'AU':'Author',
                        'DT':'Publication Date'}
        
    
    def __init__(self,fake=False):
        """ 
        Specifies whether this converter will connect to webservice or fake
        the calls for testing
        """
        self.testing = fake
            
    def _convert_one_record(self,repos_plugin, attr):
        """ 
        Creates media object with metadata 
        """
        try:
            ident = attr['ID']      
        except KeyError:
            raise MissingIDException("Record missing required ID field") 
        try:
            if type(attr['TITLE']) is list:
                # TODO: if any parts of title have commas, use semicolons instead
                desc = ", ".join(list(attr['TITLE']))
            else:
                desc = attr['TITLE']
        except KeyError:
            raise InvalidMediaObject("Mayer record %s missing required TITLE "
                                 "field" % ident)
        # TODO: fix unicode problems
        desc_str = desc.encode('ascii','ignore')
        logger.debug("ident=%s" % ident)
        
        
        meta = {}
        keyword_trans = dict([(x,x) for x in attr.keys()])
    
        for x in attr.keys():
            if x in settings.MAYER_FIELDS_TO_SKIP:
                continue
            if type(attr[x]) is list:
                joined_att = ', '.join(attr[x])
                if attr[x]:
                    meta[keyword_trans[x]] = str(joined_att.encode('ascii','ignore'))
            else:
                if attr[x]:
                    meta[keyword_trans[x]] = str(attr[x].encode('ascii','ignore'))
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug("meta=%s" % meta)
        
        
        base_record_data = {}
        base_record_data['Collection'] = 'Mayer'
        
        for m in meta:
            if m in self.base_field_trans:
                base_record_data[self.base_field_trans[m]] = meta[m]
                
        logger.debug("base_record_data=%s" % base_record_data)        
   
        
        final_datas = []
        
        final_data = []
        final_data.append(("http://rmit.edu.au/schemas/smra",'base'))
        final_data.append(base_record_data)
        final_datas.append(final_data)
        
        
           
        final_data = []
        final_data.append(("http://rmit.edu.au/schemas/smra",'system'))
        final_data.append({'ID':ident})
        final_datas.append(final_data)
          
        mayer_link = settings.MAYER_LINK % urlquote_plus(ident)
        logger.debug("mayerlink=%s" % mayer_link)
     
        final_data = []
        final_data.append(("http://rmit.edu.au/schemas/smra",'external'))
        final_data.append({'IMDB': 'http://www.imdb.com/find?s=all&q=%s'
                                         % urlquote_plus(desc),
                            'Link to Original Record':mayer_link,
                            'Label':'Mayer'})
        final_datas.append(final_data)
        
        logger.debug("final_data = %s" % final_data)
        
        obj  = repos_plugin.create_media_object(repos_name=self.REPOS_NAME,
                                               ident=ident,
                                               description=desc_str,
                                               metadata=final_datas)
        
        objs = []
        objs.append(obj)
        return objs
            
    def _make_web_call(self, query):
        """
        Make a Mayer service call (unless fake) using the query_str
        """
        if not self.testing:
            logger.debug("making call")
            timer = Timer()
            res = self.client.service.queryX(query, "null", "null", "null")
            logger.info("time for webcall = %s secs" % timer.get_elapsed_time())
        else:
            res = self.testing[query]
            logger.debug("fake res = %s" % res)
            logger.debug("fake type(res) = %s" % type(res))
            logger.debug("fake testing = %s" % self.testing)
            logger.debug("querystr = %s" % query)
            #import pdb; pdb.set_trace() 
        return res

   
    def process_one_ws_call(self,repos_plugin):
        """ 
        Make one call to the WS and process returned records
        """
        res = None
        querystr = "findall"
        if logger.isEnabledFor(logging.DEBUG):            
            logger.debug("query=%s" % querystr)
        ok_web_call = False
        timer = Timer()
        for attempts in xrange(0,settings.MAYER_CALL_ATTEMPTS):
            logger.debug("attempt=%s" % attempts)
            try:
                res = self._make_web_call(querystr)
            except Exception, e:
                import traceback,sys
                # FIXME: check if get partial results
                traceback.print_exc(file=sys.stdout)               
                message = "call failed for %s with exception  %s" % (querystr,e)
                logger.warning(message)
                logger.debug("sleeping")
                import time
                time.sleep(60)
            else:
                ok_web_call = True
                if attempts > 0:
                    logger.info("call %s okay" % attempts)
                break
        if not ok_web_call:
            message = "Error ingesting from Mayer after %s attempts" % settings.MAYER_CALL_ATTEMPTS
            logger.error(message)
            #print "res=%s" % res
            #import pdb; pdb.set_trace()  
            logger.info("time for call+ingestion = %s secs" % 
                        timer.get_elapsed_time())        
            return (None,0,0)
        else:
            logger.debug("res=%s" % res)
            result_count = 0
            new_objs_count = 0
            new_objs = []
            #print res
            for recordset in list(res.Mayer_recordset):
                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug("recordset=%s" % list(recordset))
                if  type(recordset[1]) is list:
                    for record in recordset[1]:
                        attr = dict(record)
                        try:
                            objs = self._convert_one_record(repos_plugin, attr)
                            for obj in objs:
                                new_objs_count += 1
                                new_objs.append(obj)
                        except MissingIDException:
                            raise InvalidMediaObject(
                                                     "Mayer record missing "
                                                     "required ID field "
                                                     " "
                                                     "(%s-th record)" 
                                                     % result_count)
                        result_count += 1                                            
                else:
                    attr = dict(recordset[1])
                    try:
                        objs = self._convert_one_record(repos_plugin, attr)
                        for obj in objs:
                            new_objs_count += 1
                            new_objs.append(obj)
                    except MissingIDException:
                        raise InvalidMediaObject(
                                                 "Mayer record missing "
                                                 "required ID field "
                                                 
                                                 "(%s-th record" 
                                                 % (result_count))
                    result_count += 1
            logger.info("%s elements returned" % result_count)
            logger.info("%s new objs" % new_objs_count)
            logger.info("time for call+ingestion = %s secs" % 
                        timer.get_elapsed_time())        
            return (new_objs, new_objs_count, result_count)
            
    def ingest(self,repos_plugin,system):
        try:
            sys = System.objects.get(name=system)
        except System.DoesNotExist():
            raise InvalidConfigurationException(
                                "Missing System definition for %s" % system)
        logger.debug("REPOS_NAME = %s" % self.REPOS_NAME)
        try:
            self.repos = Repository.objects.get(name=self.REPOS_NAME, system=sys)
        except Repository.DoesNotExist:
            self.repos = Repository()
            self.repos.name = self.REPOS_NAME
            self.repos.system = sys
            self.repos.save()
        # This may be needed if having problems with RMIT firewall
        if not self.testing:
            if settings.PROXY:
                proxy = urllib2.ProxyHandler(settings.PROXY)
                auth = urllib2.HTTPBasicAuthHandler()
                opener = urllib2.build_opener(proxy, auth, urllib2.HTTPHandler)
                urllib2.install_opener(opener)
            try:
                self.client = suds.client.Client(settings.MAYERWSDL,
                                        timeout=settings.SERVICE_CALL_TIMEOUT,
                                        prefixes=False)
            except Exception,e:
                logger.error("Cannot retrieve Mayer wsdl")
                raise InvalidConfigurationException(e)
                
        else:
            logger.debug("we are testing")
        (_,new_objs_count, result_count) =  self.process_one_ws_call(
                                                        repos_plugin)
        
        print "total elements returned = %s" % result_count 
        print "total new objs created = %s" % new_objs_count 
                    
        return "Hello from Mayer plugin"
        
    def get_keywordtrans(self,keyword_trans):
        """ Convert raw field names in db into cleaned up version in smra"""

        keyword_trans['Creation_Date'] = 'Creation Date' 
        keyword_trans['Corporate_Author'] = 'Corporate Author'
        return keyword_trans
    
    def setup_schemas(self,repos_plugin):
        """
        Given statistics about Records types and used attributes in the MAYER
        database, creates schemas in the SMRA.

        Note: Warning: this function is NOT reinterant, as it will create 
        additional MAYER schemas for each run.  Use with caution
        """

             
        schemas = (('Title',ParameterName.STRING,90,""),
                   ('Subtitle',ParameterName.STRING,85,""),
                   ('Subject',ParameterName.STRING,80,""),
                   ('Summary',ParameterName.STRING,75,""),
                   ('Responsibility',ParameterName.STRING,70,""),
                   ('ISBN_ISSN',ParameterName.STRING,65,""), #check underscore
                   ('Author',ParameterName.STRING,60,""),
                   ('Publication Date',ParameterName.STRING,55,""),
                   ('Publisher',ParameterName.STRING,50,""),
                   ('Contents',ParameterName.STRING,45,""),
                   ('Physical Description',ParameterName.STRING,40,""),
                   ('Source',ParameterName.STRING,35,""),
                   ('Collection',ParameterName.STRING,30,""),
                   ('Type',ParameterName.STRING,25,""))
                   
        repos_plugin.create_schema(ns="http://rmit.edu.au/schemas/smra",
                            name="base",
                            desc="Details",
                            parameters=schemas)

        
        schemas = (('Issue',ParameterName.STRING,90,""),
                   ('Journal',ParameterName.STRING,85,""),
                   ('Page',ParameterName.STRING,80,""),
                   ('Volume',ParameterName.NUMERIC,75,""),
                   ('Newspaper',ParameterName.STRING,70,""),
                   ('Performer',ParameterName.STRING,65,""),
                   ('Alternate Title',ParameterName.STRING,60,""),
                   ('Product Code',ParameterName.STRING,55,""),
                   ('Credits',ParameterName.STRING,50,""),
                   ('Donation',ParameterName.STRING,45,""),
                   ('Language',ParameterName.STRING,40,""),
                   ('Comment',ParameterName.STRING,35,""),
                   ('Notes',ParameterName.STRING,30,""),
                   ('Production Year',ParameterName.YEAR,25,""),
                   ('First Release Year',ParameterName.YEAR,20,""))
                   
        repos_plugin.create_schema(ns="http://rmit.edu.au/schemas/smra",
                            name="extended",
                            desc="Extended Details",
                            parameters=schemas)

        schemas = (('Record Type',ParameterName.STRING,90,""),
                   ('Created',ParameterName.DATE,80,""),
                   ('ID',ParameterName.STRING,75,""),
                   ('Call No',ParameterName.STRING,70,""))
                   
        repos_plugin.create_schema(ns="http://rmit.edu.au/schemas/smra",
                            name="system", 
                            desc="System Details",
                            parameters=schemas)


        #TODO: this schema could be created in ReposPlugin
        schemas  = (('IMDB',
                    ParameterName.LINK,90,""),
                   ('Link to Original Record',ParameterName.LINK,80,""))
       
        repos_plugin.create_schema(ns="http://rmit.edu.au/schemas/smra",
                            name="external",
                            desc="External Collections",
                            parameters=schemas)
        
        
        
    
    